/*
 Автор: Швейкин Евгений Юрьевич

 Функциональное описание:     
    Управление модулем
    адрес   Данные
    0       период
    1       длительность 1
    2       задержка до первого импульса
    3       фаза (смещение фронта 1)
    4       количество импульсов
    5-15    резерв
*/
//--------------------------------------------------------------------------------------------------
// Описание структуры порта канала FC
// ********************** МАКРОСЫ ******************************************************************
    // Описывает процесс модуля выделения фронтов сигнала. 
    // Так же описывает создание новых сигналов с именем <имя сигнала> + <_rise> (<_fall>, <_edge>)
    `define front(clk, name)                    \
    logic name``_0;                             \
    logic name``_1;                             \
    logic name``_rise;                          \
    logic name``_fall;                          \
    logic name``_edge;                          \
    always_ff @(posedge clk)                    \
    begin                                       \
        name``_0 <= name;                       \
        name``_1 <= name``_0;                   \
        name``_rise <= name``_0 & (~name``_1);  \
        name``_fall <= name``_1 & (~name``_0);  \
        name``_edge <= name``_0 ^ name``_1;     \
    end
// *********************** НАЧАЛО МОДУЛЯ ***********************************************************
module mod_impulse_gen_group
(   input  logic            clk,
    input  logic            reset_n,
    
    input  logic [31:0]     sync_imp,
    // Управление модулем
    input  logic [3:0]      addr,
    input  logic [31:0]     data,
    input  logic            wre,
    
    // Сигналы шины master авалон
    output logic            imp
);
 
//  ********************* КОНСТАНТЫ ****************************************************************
    // localparam fifo_size_lp      = 1024;
    // localparam fifo_wa_lp       = $clog2(fifo_size_lp);
//  ********************* ФУНКЦИИ ******************************************************************

//  ********************* ТИПЫ ДАННЫХ **************************************************************

// ********************** ПЕРЕМЕННЫЕ ***************************************************************
    enum logic [7:0]    {   F_DELAY_IMP_s       = 8'b0000_0001,
                            PHASE_IMP_s         = 8'b0000_0010,
                            PERIOD_IMP_s        = 8'b0000_0100,
                            UP_IMP_s            = 8'b0000_1000,
                            F_DELAY_GROUP_s     = 8'b0001_0000,
                            PERIOD_GROUP_s      = 8'b0010_0000,
                            PHASE_GROUP_s       = 8'b0100_0000,
                            CNT_CHECK_s           = 8'b1000_0000
                        } state_gen;
                        
    logic               wre_0;
    logic               rst_time_cnt;
    logic               rst_time_cnt_group;
    logic               start;
    
    logic [31:0]        time_cnt;    
    logic [31:0]        time_cnt_group;    
     
    logic [31:0]        f_delay_imp;   
    logic [31:0]        phase_imp;
    logic [31:0]        period_imp;
    logic [31:0]        up_imp;
    logic [31:0]        num_imp;
    logic [31:0]        cnt_imp;
    logic [7:0]         sync_num_imp;    
    
    logic [31:0]        f_delay_group;    
    logic [31:0]        period_group;
    logic [31:0]        phase_group;   
    
    logic [31:0]        down_imp; 
// ********************* ИНИЦИАЛИЗАЦИЯ ПЕРЕМЕННЫХ ДЛЯ СИМУЛЯТОРА ***********************************

    // initial time_cnt     <= 32'h00FFFFF;

//  ********************* ASSIGN *******************************************************************
    
// ********************* ПОДКЛЮЧАЕМЫЕ МОДУЛИ *******************************************************

    assign start   = sync_imp[sync_num_imp];
// ********************* ПРОЦЕССЫ ******************************************************************
    `front (clk, start)
    
    always_ff @(posedge clk)
    begin
        down_imp    <= period_imp - up_imp - phase_imp;
        wre_0       <= wre;
        if (wre_0) begin
            case (addr - 4'd1)
                4'd0:   f_delay_group     <= {data[7:0], data[15:8], data[23:16], data[31:24]};
                4'd1:   period_imp      <= {data[7:0], data[15:8], data[23:16], data[31:24]};
                4'd2:   phase_imp       <= {data[7:0], data[15:8], data[23:16], data[31:24]};
                4'd3:   up_imp          <= {data[7:0], data[15:8], data[23:16], data[31:24]};
                4'd4:   num_imp         <= {data[7:0], data[15:8], data[23:16], data[31:24]};
                4'd5:   sync_num_imp    <= data[31:24];
                
                4'd6:   f_delay_imp     <= {data[7:0], data[15:8], data[23:16], data[31:24]};
                4'd7:   period_group    <= {data[7:0], data[15:8], data[23:16], data[31:24]};
                4'd8:   phase_group     <= {data[7:0], data[15:8], data[23:16], data[31:24]};
                
                // 4'd0:   f_delay_imp     <= {data[7:0], data[15:8], data[23:16], data[31:24]};
                // 4'd1:   phase_imp       <= {data[7:0], data[15:8], data[23:16], data[31:24]};
                // 4'd2:   period_imp      <= {data[7:0], data[15:8], data[23:16], data[31:24]};
                // 4'd3:   up_imp          <= {data[7:0], data[15:8], data[23:16], data[31:24]};
                // 4'd4:   num_imp         <= {data[7:0], data[15:8], data[23:16], data[31:24]};
                // 4'd5:   sync_num_imp    <= data[31:24];
                
                // 4'd6:   f_delay_group   <= {data[7:0], data[15:8], data[23:16], data[31:24]};
                // 4'd7:   phase_group     <= {data[7:0], data[15:8], data[23:16], data[31:24]};
                // 4'd8:   period_group    <= {data[7:0], data[15:8], data[23:16], data[31:24]};
                default: ;
            endcase;
        end
        else;
    end

    
    always_ff @(posedge clk, posedge rst_time_cnt)
    begin
        if (rst_time_cnt)
            time_cnt    <= 32'd0;
        else
            time_cnt    <= time_cnt + 32'd1;
    end
    
    always_ff @(posedge clk, posedge rst_time_cnt_group)
    begin
        if (rst_time_cnt_group)
            time_cnt_group    <= 32'd0;
        else
            time_cnt_group    <= time_cnt_group + 32'd1;
    end
    
    // Определение следующего состояния автомата
    always_ff @(posedge clk, negedge reset_n)       
    begin    
        if (!reset_n) begin
            state_gen       <= F_DELAY_GROUP_s;
            rst_time_cnt    <= 1'b1;
            rst_time_cnt_group    <= 1'b1;
        end
        else if (start_rise) begin
            state_gen       <= F_DELAY_GROUP_s;
            rst_time_cnt    <= 1'b1;
            rst_time_cnt_group    <= 1'b1;
        end
        else begin
            rst_time_cnt    <= 1'b0;
            rst_time_cnt_group    <= 1'b0;
            imp             <= 1'b0;
            unique case (state_gen)
                F_DELAY_GROUP_s:          
                begin
                    if (time_cnt_group >= f_delay_group) begin
                        state_gen           <= PHASE_GROUP_s;
                        rst_time_cnt        <= 1'b1;
                        rst_time_cnt_group  <= 1'b1;
                    end
                    else;
                end
                
                PHASE_GROUP_s:
                begin
                    if (time_cnt >= phase_group) begin
                        state_gen <= F_DELAY_IMP_s;
                        rst_time_cnt    <= 1'b1;
                    end
                    else;
                end
                
                F_DELAY_IMP_s:
                begin
                    cnt_imp         <= 32'd0;
                    if (time_cnt >= f_delay_imp) begin
                        if (|phase_imp)
                            state_gen <= PHASE_IMP_s;
                        else
                            state_gen <= UP_IMP_s;
                        rst_time_cnt    <= 1'b1;
                    end
                    else;
                end
                
                PHASE_IMP_s:
                begin
                    if (time_cnt >= phase_imp - 1) begin
                        state_gen <= UP_IMP_s;
                        rst_time_cnt    <= 1'b1;
                    end
                    else;
                end
                
                UP_IMP_s:
                begin
                    imp <= 1'b1;
                    if (time_cnt >= up_imp - 1) begin
                        state_gen <= PERIOD_IMP_s;
                        rst_time_cnt    <= 1'b1;
                    end
                    else;
                end

                PERIOD_IMP_s:
                begin
                    if (time_cnt >= down_imp) begin
                        state_gen <= CNT_CHECK_s;
                        cnt_imp     <= cnt_imp + 32'd1; 
                        rst_time_cnt    <= 1'b1;
                    end
                    else;
                end
                
                CNT_CHECK_s:
                begin
                    if (cnt_imp < num_imp)
                        if (|phase_imp)
                            state_gen <= PHASE_IMP_s;
                        else
                            state_gen <= UP_IMP_s;
                    else
                        state_gen <= PERIOD_GROUP_s;
                end
                
                PERIOD_GROUP_s:
                begin
                    if (time_cnt_group >= period_group) begin
                        state_gen <= F_DELAY_GROUP_s;
                        rst_time_cnt_group    <= 1'b1;
                    end
                    else;
                end
                default;
            endcase
        end
    end    
    
endmodule 